/*
 * © 2024 huayunliufeng保留所有权利, 依据MIT许可证发布。
 * 请勿更改或删除版权声明或此文件头。
 * 此代码是免费软件, 您可以重新分发和/或修改它。
 * 开源是希望它有用, 但不对代码做任何保证。
 * 如有疑问请联系: huayunliufeng@163.com
 */

package io.github.huayunliufeng.dbinfo.dbinfo;

import io.github.huayunliufeng.common.utils.HylfDataUtil;
import io.github.huayunliufeng.common.utils.HylfFunUtil;
import io.github.huayunliufeng.dbinfo.model.FunctionColumnInfo;
import io.github.huayunliufeng.dbinfo.model.FunctionsInfo;

import java.sql.Connection;
import java.sql.ResultSet;
import java.util.List;
import java.util.Map;

/**
 * <p>检索给定目录中可用的系统和用户功能的描述。</p>
 * <p>只返回匹配模式和函数名条件的系统和用户函数描述。它们按FUNCTION_CAT、FUNCTION_SCHEMA、FUNCTION_NAME和SPECIFIC_ NAME排序。</p>
 * <p>用户可能没有权限执行getFunctions返回的任何函数</p>
 *
 * @author zhongq
 * @datetime 2024/4/1 10:04
 */
public class ObtainFunctionsInfo extends ObtainDbInfo {
    private static Map<Connection, ObtainFunctionsInfo> obtainFunctionsInfoMap = null;

    private Map<String, List<FunctionsInfo>> functionsInfoMap = null;

    private Map<String, List<FunctionColumnInfo>> functionColumnInfoMap = null;

    protected ObtainFunctionsInfo(Connection conn) {
        super(conn);
    }

    public static ObtainFunctionsInfo build(Connection conn) {
        obtainFunctionsInfoMap = HylfFunUtil.mapAddValue(obtainFunctionsInfoMap, conn, () -> new ObtainFunctionsInfo(conn));
        return obtainFunctionsInfoMap.get(conn);
    }

    /**
     * <p>检索给定目录中可用的系统和用户功能的描述。</p>
     * <p>只返回匹配模式和函数名条件的系统和用户函数描述。它们按FUNCTION_CAT、function_schema、FUNCTION_NAME和SPECIFIC_ NAME排序。</p>
     * <p>用户可能没有权限执行getFunctions返回的任何函数</p>
     *
     * @param catalog             目录名称;必须与存储在数据库中的目录名称匹配;“”检索那些没有目录的;null表示不应使用目录名称来缩小搜索范围
     * @param schemaPattern       模式名称模式;必须与存储在数据库中的模式名称匹配;""检索那些没有模式的;null表示不应该使用模式名称来缩小搜索范围
     * @param functionNamePattern 函数名模式;必须匹配的函数名称，因为它是存储在数据库中
     * @return 每一行都是一个函数描述
     */
    public List<FunctionsInfo> getFunctions(String catalog, String schemaPattern, String functionNamePattern) {
        String logFormat = "获取数据库Functions信息失败。[catalog = {}, schemaPattern = {}, functionNamePattern = {}]";
        return HylfFunUtil.methodVoidReturnExec(() -> {
            String key = String.join("-", catalog, schemaPattern, functionNamePattern);
            functionsInfoMap = HylfFunUtil.mapAddValue(functionsInfoMap, key, () -> {
                ResultSet resultSet = HylfFunUtil.methodVoidReturnExec(
                        () -> databaseMetaData.getFunctions(catalog, schemaPattern, functionNamePattern)
                        , logFormat, catalog, schemaPattern, functionNamePattern);
                List<FunctionsInfo> functionsInfoList = HylfDataUtil.autoSetValue(resultSet, FunctionsInfo.class);
                for (FunctionsInfo functionsInfo : functionsInfoList) {
                    String functionCat = functionsInfo.getFunctionCat();
                    String functionSchem = functionsInfo.getFunctionSchem();
                    String functionName = functionsInfo.getFunctionName();
                    functionsInfo.setFunctionColumnInfoList(getFunctionColumns(functionCat, functionSchem, functionName, "%"));
                }
                HylfFunUtil.autoClose(resultSet);
                return functionsInfoList;
            });
            return functionsInfoMap.get(key);
        });
    }

    /**
     * <p>检索给定目录中可用的系统和用户功能的描述。</p>
     * <p>只返回匹配模式和函数名条件的系统和用户函数描述。它们按FUNCTION_CAT、function_schema、FUNCTION_NAME和SPECIFIC_ NAME排序。</p>
     * <p>用户可能没有权限执行getFunctions返回的任何函数</p>
     *
     * @param functionNamePattern 函数名模式;必须匹配的函数名称，因为它是存储在数据库中
     * @return 每一行都是一个函数描述
     */
    public List<FunctionsInfo> getFunctions(String functionNamePattern) {
        return getFunctions(getCatalog(), getSchema(), functionNamePattern);
    }

    /**
     * <p>检索给定目录的系统或用户函数参数和返回类型的描述。</p>
     * <p>只返回与模式、函数和参数名条件匹配的描述。它们按FUNCTION_CAT、function_schema、FUNCTION_NAME和SPECIFIC_ NAME排序。
     * 其中，返回值(如果有的话)是第一个。接下来是按调用顺序的参数描述。列描述按照列号顺序进行。</p>
     * <p>ResultSet中的每一行都是一个参数说明列。</p>
     *
     * @param catalog             目录名称;必须与存储在数据库中的目录名称匹配;“”检索那些没有目录的;null表示不应使用目录名称来缩小搜索范围
     * @param schemaPattern       模式名称模式;必须与存储在数据库中的模式名称匹配;""检索那些没有模式的;null表示不应该使用模式名称来缩小搜索范围
     * @param functionNamePattern 过程名称模式;必须匹配的函数名称，因为它是存储在数据库中
     * @param columnNamePattern   参数名称模式;必须匹配存储在数据库中的参数或列名
     * @return 每行描述一个用户函数参数、列或返回类型
     */
    public List<FunctionColumnInfo> getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) {
        String logFormat = "获取数据库FunctionColumns信息失败。[catalog = {}, schemaPattern = {}, functionNamePattern = {}, columnNamePattern = {}]";
        return HylfFunUtil.methodVoidReturnExec(() -> {
            String key = String.join("-", catalog, schemaPattern, functionNamePattern);
            functionColumnInfoMap = HylfFunUtil.mapAddValue(functionColumnInfoMap, key, () -> {
                ResultSet resultSet = HylfFunUtil.methodVoidReturnExec(
                        () -> databaseMetaData.getFunctionColumns(catalog, schemaPattern, functionNamePattern, columnNamePattern)
                        , logFormat, catalog, schemaPattern, functionNamePattern, columnNamePattern);
                List<FunctionColumnInfo> functionColumnInfoList = HylfDataUtil.autoSetValue(resultSet, FunctionColumnInfo.class);
                HylfFunUtil.autoClose(resultSet);
                return functionColumnInfoList;
            });
            return functionColumnInfoMap.get(key);
        });
    }

    /**
     * <p>检索给定目录的系统或用户函数参数和返回类型的描述。</p>
     * <p>只返回与模式、函数和参数名条件匹配的描述。它们按FUNCTION_CAT、function_schema、FUNCTION_NAME和SPECIFIC_ NAME排序。
     * 其中，返回值(如果有的话)是第一个。接下来是按调用顺序的参数描述。列描述按照列号顺序进行。</p>
     * <p>ResultSet中的每一行都是一个参数说明列。</p>
     *
     * @param functionNamePattern 过程名称模式;必须匹配的函数名称，因为它是存储在数据库中
     * @param columnNamePattern   参数名称模式;必须匹配存储在数据库中的参数或列名
     * @return 每行描述一个用户函数参数、列或返回类型
     */
    public List<FunctionColumnInfo> getFunctionColumns(String functionNamePattern, String columnNamePattern) {
        return getFunctionColumns(getCatalog(), getSchema(), functionNamePattern, columnNamePattern);

    }

    /**
     * <p>检索给定目录的系统或用户函数参数和返回类型的描述。</p>
     * <p>只返回与模式、函数和参数名条件匹配的描述。它们按FUNCTION_CAT、function_schema、FUNCTION_NAME和SPECIFIC_ NAME排序。
     * 其中，返回值(如果有的话)是第一个。接下来是按调用顺序的参数描述。列描述按照列号顺序进行。</p>
     * <p>ResultSet中的每一行都是一个参数说明列。</p>
     *
     * @return 每行描述一个用户函数参数、列或返回类型
     */
    public List<FunctionColumnInfo> getFunctionColumns() {
        return getFunctionColumns(getCatalog(), getSchema(), "%", "%");
    }
}
